// -*- mode:c++ -*-

// Copyright (c) 2009 The University of Edinburgh
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: Lei Zhang

let {{

updateLrCode = 'LR = PC + 4;'

}};

// Instructions that unconditionally branch relative to the current PC.
def format BranchPCRel(br_code, inst_flags = []) {{
    inst_flags += ('IsUncondControl', 'IsDirectControl')
    basic_code = br_code

    isCall = 0
    for val in inst_flags:
        if val == 'IsCall':
            isCall = True

    # The version that does not update LR
    (header_output, decoder_output, decode_block, exec_output) = \
        GenAluOp(name, Name, 'BranchPCRel', basic_code, inst_flags,
                 BasicDecode, BasicConstructor)

    # The version that does the update
    update_code = basic_code + updateLrCode
    update_flags = inst_flags + [ 'IsCall' ]
    (header_output_up, decoder_output_up, _, exec_output_up) = \
        GenAluOp(name, Name, 'BranchPCRel', update_code,
                 update_flags, BasicDecode, BasicConstructor)

    if isCall:
        header_output = header_output_up
        decoder_output = decoder_output_up
        exec_output = exec_output_up
}};

// Instructions that unconditionally branch to a specific address.
def format BranchNonPCRel(br_code, inst_flags = []) {{
    inst_flags += ('IsUncondControl', 'IsDirectControl')
    basic_code = br_code

    isCall = 0
    for val in inst_flags:
        if val == 'IsCall':
            isCall = True

    # The version that does not update LR
    (header_output, decoder_output, decode_block, exec_output) = \
        GenAluOp(name, Name, 'BranchNonPCRel', basic_code, inst_flags,
                 BasicDecode, BasicConstructor)
    
    # The version that does the update
    update_code = basic_code + updateLrCode
    update_flags = inst_flags + [ 'IsCall' ]
    (header_output_up, decoder_output_up, _, exec_output_up) = \
        GenAluOp(name, Name, 'BranchNonPCRel', update_code,
                 update_flags, BasicDecode, BasicConstructor)

    if isCall:
        header_output = header_output_up
        decoder_output = decoder_output_up
        exec_output = exec_output_up
}};

let {{

# Check the active flag register (AFR) allow the
# branch to be taken. Also, in certain situations, decrement the count
# register too. This takes place in ctrOk within BranchCond classes.
def GetCondCode(br_code):
    cond_code =  'uint8_t afr = AFR;\n'
    cond_code += 'bool afr_ok = afrOk(afr);\n'
    cond_code += 'if(afr_ok) {\n'
    cond_code += '    ' + br_code + '\n'
    cond_code += '} else {\n'
    cond_code += '    NPC = NPC;\n'
    cond_code += '}\n'
    return cond_code

}};

// Instructions that conditionally branch relative to the current PC based on
// the active flag register (AFR)
def format BranchPCRelCond(br_code, inst_flags = []) {{
    inst_flags += ('IsCondControl', 'IsDirectControl')
    basic_code = GetCondCode(br_code)
    
    isCall = 0
    for val in inst_flags:
        if val == 'IsCall':
            isCall = True
    
    # The version that does not update LR
    (header_output, decoder_output, decode_block, exec_output) = \
        GenAluOp(name, Name, 'BranchPCRelCond', basic_code, inst_flags,
                 BasicDecode, BasicConstructor)

    # The version that does the update
    update_code = basic_code + updateLrCode
    update_flags = inst_flags + [ 'IsCall' ]
    (header_output_up, decoder_output_up, _, exec_output_up) = \
        GenAluOp(name, Name, 'BranchPCRelCond', update_code,
                 update_flags, BasicDecode, BasicConstructor)

    if isCall:
        header_output = header_output_up
        decoder_output = decoder_output_up
        exec_output = exec_output_up
}};

